package com.yya.ratelimiter.service;

import com.yya.ratelimiter.annotation.RateLimit;
import com.yya.ratelimiter.exception.RateLimitException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Autor: yuanyongan
 * @Description: 令牌桶算法: 令牌桶更多的是用于冷启动，逐步提升流量
 * 1、桶中令牌按一定速率增加，桶中令牌最大数量固定，满了就不加了
 * 2、请求过来的时候，桶中有令牌，就可以获取令牌通过
 * 3、要考虑并发情况下，令牌数减少的情况
 *
 * 令牌桶的容量是最大可能处理突发流量的上限
 * @Date 2023/1/12 9:57
 */

@Service
@Data
@Slf4j
public class TokenBucket implements LimitationAlgorithm{
    /**
     * 线程池对象注入
     */
    @Resource
    Executor executor;

    // 当前桶中令牌数量,按一定速率增加
    final AtomicInteger curTokenNum;
    // 令牌增加速率,每秒的数量
    int ratio = 32;
    // 记录上次统计时间的毫秒
    long lastTime;
    // 令牌桶容量
    int capacity = 1024;
    // 可重入锁
    ReentrantLock lock = new ReentrantLock();

    public TokenBucket(){
        this.lastTime = System.currentTimeMillis();
        curTokenNum = new AtomicInteger(0);
    }

    /**
     * 匀速给令牌桶添加令牌
     */
    public void tokenNumIncr(){
        curTokenNum.addAndGet(Math.min(ratio, capacity - curTokenNum.get()));
        log.info("当前桶内令牌数：{}", curTokenNum.get());
    }

    /**
     * 请求获取令牌
     * @param rateLimit 限流注解参数
     * @param methodSignature 切面参数
     */
    @Override
    public void acquire(RateLimit rateLimit, MethodSignature methodSignature) {
        synchronized (curTokenNum){
            int tmp = curTokenNum.get();
            if (curTokenNum.get() == 0){
                log.info("当前接口达到最大限流次数");
                throw new RateLimitException(500, "当前接口达到最大限流次数");
            }
            curTokenNum.addAndGet(-1);
            log.info("当前令牌还剩:{}", curTokenNum.get());
        }
    }

    @Override
    public void logInfo() {

    }
}
